package org.jeecg.modules.payment.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.boot.starter.rabbitmq.client.RabbitMqClient;
import org.jeecg.common.base.BaseMap;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.rental.vo.LoginRentalAccount;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.payment.bo.PaymentParams;
import org.jeecg.modules.payment.entity.PaymentConfig;
import org.jeecg.modules.payment.entity.PaymentRecord;
import org.jeecg.modules.payment.enums.BizTypeEnums;
import org.jeecg.modules.payment.enums.PayChannelEnums;
import org.jeecg.modules.payment.enums.PayStatusEnums;
import org.jeecg.modules.payment.mapper.PaymentRecordMapper;
import org.jeecg.modules.payment.req.PaymentReq;
import org.jeecg.modules.payment.service.IPaymentConfigService;
import org.jeecg.modules.payment.service.IPaymentRecordService;
import org.jeecg.modules.payment.strategy.PaymentContext;
import org.jeecg.modules.payment.util.SnUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * @Description: 支付记录
 * @Author: sean
 * @Date:   2024-08-28
 * @Version: V1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PaymentRecordServiceImpl extends ServiceImpl<PaymentRecordMapper, PaymentRecord> implements IPaymentRecordService {

    private final IPaymentConfigService paymentConfigService;
    private final RabbitMqClient rabbitMqClient;
    private final SnUtil snUtil;

    @Override
    public IPage<PaymentRecord> selectJoinPage(Page<PaymentRecord> page, Wrapper wrapper) {
        IPage<PaymentRecord> pageList = baseMapper.selectJoinPage(page, wrapper);
        if(ObjectUtil.isNotEmpty(pageList.getRecords())){
            pageList.getRecords().stream().forEach(item->{
                try{
                    PayStatusEnums statusEnums = PayStatusEnums.getEnumByValue(item.getPayStatus());
                    if(oConvertUtils.isNotEmpty(statusEnums)){
                        item.setPayStatusTxt(statusEnums.getLabel());
                    }
                    item.setChannelTxt(PayChannelEnums.getEnumByValue(item.getChannelCode()).getLabel());
                    PayChannelEnums channelEnums = PayChannelEnums.getEnumByValue(item.getChannelCode());
                    if(oConvertUtils.isNotEmpty(channelEnums)){
                        item.setChannelTxt(channelEnums.getLabel());
                    }
                    BizTypeEnums bizTypeEnums = BizTypeEnums.getEnumByValue(item.getBizType());
                    if(oConvertUtils.isNotEmpty(bizTypeEnums)){
                        item.setBizTypeTxt(bizTypeEnums.getLabel());
                    }
                }catch (JeecgBootException ex){
                    ex.printStackTrace();
                }
            });
        }
        return pageList;
    }

    @Override
    public JsonNode pay(PaymentReq req) {
        PaymentParams params = new PaymentParams();
        BeanUtil.copyProperties(req,params);
        PaymentRecord paymentRecord = new PaymentRecord();
        LoginRentalAccount rentalAccount = (LoginRentalAccount) SecurityUtils.getSubject().getPrincipal();
        paymentRecord.setAccountId(rentalAccount.getId().toString());
        params.setOpenId(rentalAccount.getWxOpenId());
        paymentRecord.setBizType(req.getBizType());
        paymentRecord.setBizId(req.getBizId());
        paymentRecord.setChannelCode(req.getPayChannel());
        String tradeNo = snUtil.getTradeCode();
        params.setTradeNo(tradeNo);
        paymentRecord.setPayCode(tradeNo);
        paymentRecord.setPayStatus("0");
        paymentRecord.setPayAmount(req.getPayAmount());
        paymentRecord.setPayTime(LocalDateTime.now());
        baseMapper.insert(paymentRecord);
        PaymentConfig paymentConfig = paymentConfigService
                .getConfigByChannel(TenantContext.getTenant(), req.getPayChannel());
        PaymentContext paymentContext = new PaymentContext(paymentConfig);
        JsonNode jsonNode = paymentContext.payment(params);
        return jsonNode;
    }


    @Override
    public void payCallback(HttpServletRequest request, HttpServletResponse response, String bizType, String payChannel, String payWay, String tenantId) {
        //业务类型不同，对应的mq队列不同
        BizTypeEnums bizTypeEnum = BizTypeEnums.getEnumByValue(bizType);
        PaymentConfig paymentConfig = paymentConfigService
                .getConfigByChannel(tenantId, payChannel);
        if (paymentConfig == null) {
            throw new JeecgBootException("未找到对应的支付配置");
        }
        //准备支付类
        PaymentContext paymentContext = new PaymentContext(paymentConfig);
        try {
            JsonNode params = paymentContext.payCallBack(request, payWay, tenantId);
            String returnCode = params.get("returnCode").asText();
            if ("0".equals(returnCode)) {
                //查询支付记录
                String payCode = params.get("payCode").asText();
                String otherCode = params.get("otherCode").asText();
                BigDecimal payAmount = params.get("payAmount").decimalValue();
                PaymentRecord paymentRecord = baseMapper.getOneNoTenant(new QueryWrapper<PaymentRecord>()
                        .eq("tenant_id", tenantId)
                        .eq("pay_code", payCode));
                if(ObjectUtil.isEmpty(paymentRecord)){
                    throw new JeecgBootException("没有找到对应的预支付记录");
                }
                if (paymentRecord.getPayAmount().compareTo(payAmount) != 0 ){
                    log.error("支付金额不符:{},{}", paymentRecord.getPayAmount(), payAmount);
                    throw new JeecgBootException("支付金额不符");
                }
                //修改支付时间和支付状态
                LocalDateTime now = LocalDateTime.now();
                paymentRecord.setPayStatus("1");
                paymentRecord.setPayTime(now);
                paymentRecord.setOtherCode(otherCode);
                baseMapper.updateById(paymentRecord);
                //使用mq同时租车订单修改订单状态和添加用户套餐 feign mq  重试  请求
                log.info("发送mq修改订单信息");
                BaseMap json = new BaseMap();
                json.put("tenantId", tenantId);
                json.put("orderId", paymentRecord.getBizId());
                json.put("payTime", now);
                json.put("payAmount", payAmount);
                rabbitMqClient.sendMessage(bizTypeEnum.getQueueName(), json);
                // 3.应答工行：在接收到工行的支付结果通知后，一定要返回应答，否则工行会认为该通知失败，在一定时间区间内多次发起通知
                paymentContext.callbackResponse(response, params, true);
            } else {
                // 3.应答工行：在接收到工行的支付结果通知后，一定要返回应答，否则工行会认为该通知失败，在一定时间区间内多次发起通知
                log.error("交易失败，返回码:{}", params.get("returnCode").asText());
                paymentContext.callbackResponse(response, params, false);
            }
        } catch (Exception e) {
            // 捕获异常并回复第三方回调接口表示失败
            log.error("处理租车回调时发生异常", e);
            throw new JeecgBootException("处理租车回调时发生异常");
        }
    }


}
